package com.zfei.gfb.util;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.StringRedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;

import java.time.Duration;
import java.util.List;

/**
 * @author fgh
 */
public class RedisLock {

    private static final Logger logger = LoggerFactory.getLogger(RedisLock.class);

    private static final Duration TIMEOUT = Duration.ofSeconds(3);

    private String prefix;

    private StringRedisTemplate redis;

    public RedisLock(RedisConnectionFactory factory) {
        this("LOCK:", factory);
    }

    public RedisLock(String prefix, RedisConnectionFactory factory) {
        this.prefix = prefix;
        this.redis = new StringRedisTemplate(factory);
    }

    public String retain(String point) {
        return this.retain(point, TIMEOUT);
    }

    /**
     * 获取分布式锁
     *
     * @param point   服务名
     * @param timeout 超时秒
     * @return key 锁键
     */
    public String retain(String point, Duration timeout) {
        String key = this.prefix + point;
        Boolean isLocked = this.redis.hasKey(key);
        logger.debug("LOCK_TRY: key={}, isLocked={}", key, isLocked);

        if (isLocked != null && !isLocked) {
            List<Object> pipelined = this.redis.executePipelined((RedisCallback<Object>) connection -> {
                StringRedisConnection conn = (StringRedisConnection) connection;
                conn.incr(key);
                conn.expire(key, timeout.getSeconds());
                return null;
            });
            logger.debug("LOCK_INC: key={}, pipelined={}", key, Gsons.toJson(pipelined));
            if (!pipelined.isEmpty() && pipelined.get(0).toString().equals("1")) {
                logger.debug("LOCK_SET: key={}, timeout={}", key, timeout);
                return key;
            }
        }

        logger.debug("LOCK_ERR: key={}", key);
        return null;
    }

    /**
     * 释放分布式锁
     *
     * @param key 锁键
     */
    public void release(String key) {
        if (StringUtils.isBlank(key)) {
            return;
        }

        this.redis.delete(key);
        logger.debug("UNLOCK: key={}", key);
    }
}
